﻿using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Windows;
using System.IO;
using Microsoft.Win32;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using System.Drawing.Text;
using System.Xml.Linq;
using System.Linq;
using System.Collections.Generic;
using System.Xml;
using System.Reflection;
using System.Windows.Media;
using System.Collections;
using System.Threading;

namespace FontInstaller
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : System.Windows.Window
    {
        [DllImport("gdi32.dll", EntryPoint="AddFontResourceW", SetLastError=true)]
        public static extern int AddFontResource([In][MarshalAs(UnmanagedType.LPWStr)] string lpFileName);


        [DllImport("gdi32.dll", EntryPoint="RemoveFontResourceW", SetLastError=true)]
        public static extern int RemoveFontResource([In][MarshalAs(UnmanagedType.LPWStr)] string lpFileName);

        public ObservableCollection<BoolStringClass> TheList { get; set; }

        private System.Windows.Forms.NotifyIcon ni = new System.Windows.Forms.NotifyIcon();

        private String fontsFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\DodajFont\fonts\";
        private String appFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\DodajFont\";
        private String settingsFilePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\DodajFont\Fonts.xml";

        void DataWindow_Closing(object sender, CancelEventArgs e)
        {
            try
            {
                if (ni != null)
                {
                    ni.Visible = false;
                    ni.Icon = null; // required to make icon disappear
                    ni.Dispose();
                    ni = null;
                }
            }
            catch (Exception ex)
            {
                lbl_statusBar.Text = "Closing ERROR " + ex.ToString();
                stb_status.Background = Brushes.LightCoral;
            }
        }

        public static Version GetPublishedVersion()
        {
            XmlDocument xmlDoc = new XmlDocument();
            Assembly asmCurrent = System.Reflection.Assembly.GetExecutingAssembly();
            string executePath = new Uri(asmCurrent.GetName().CodeBase).LocalPath;

            xmlDoc.Load(executePath + ".manifest");
            string retval = string.Empty;
            if (xmlDoc.HasChildNodes)
            {
                retval = xmlDoc.ChildNodes[1].ChildNodes[0].Attributes.GetNamedItem("version").Value.ToString();
            }
            return new Version(retval);
        }

        public MainWindow()
        {
            InitializeComponent();

            this.Title = "Dodaj Font - v" + GetPublishedVersion();

            ni.Icon = new System.Drawing.Icon(@"Main.ico");
            ni.Visible = true;
            ni.Click +=
                delegate(object sender, EventArgs args)
                {
                    this.Show();
                    this.WindowState = WindowState.Normal;
                };
            CreateCheckBoxList();
        }

        public class BoolStringClass : INotifyPropertyChanged
        {
            public string TheText { get; set; }

            public string ThePath { get; set; }

            //Provide change-notification for IsSelected
            private bool _fIsSelected = false;
            public bool IsSelected
            {
                get { return _fIsSelected; }
                set
                {
                    _fIsSelected = value;
                    this.OnPropertyChanged("IsSelected");
                }
            }

            #region INotifyPropertyChanged

            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged(string strPropertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(strPropertyName));
            }

            #endregion

            public override string ToString()
            {
                return TheText;
            }
        }

        public void CreateCheckBoxList()
        {
            TheList = new ObservableCollection<BoolStringClass>();

            //Provide change-notification for the collection (added to / removed from the list)
            TheList.CollectionChanged += TheList_CollectionChanged;

            try
            {
                Directory.CreateDirectory(appFolderPath);
                if (File.Exists(settingsFilePath))
                {
                    XElement xEle = XElement.Load(settingsFilePath);
                    IEnumerable<XElement> fonts = xEle.Elements();

                    foreach (var font in fonts)
                    {

                        if (font.Element("IsSelected").Value == "true")
                        {
                            AddFont(font.Element("FontPath").Value);
                            TheList.Add(new BoolStringClass { IsSelected = true, TheText = font.Element("FontName").Value, ThePath = font.Element("FontPath").Value });
                        }
                        else
                        {
                            RemoveFont(font.Element("FontPath").Value);
                            TheList.Add(new BoolStringClass { IsSelected = false, TheText = font.Element("FontName").Value, ThePath = font.Element("FontPath").Value });
                        }
                    }
                    SortListbox();
                }
                else
                {
                    XNamespace empNM = "urn:lst-emp:emp";
                    XDocument xDoc = new XDocument(
                                new XDeclaration("1.0", "UTF-16", null),
                                new XElement(empNM + "Fontovi"));
                    xDoc.Save(settingsFilePath);
                }


                //Add some items - here
                //TheList.Add(new BoolStringClass { IsSelected = true, TheText = "Some text for item #1" });

                //Provide change-notification for all members
                foreach (var item in TheList)
                    item.PropertyChanged += TheList_Item_PropertyChanged;

                this.DataContext = this;
            }
            catch (Exception error)
            {
                MessageBox.Show("Init ERROR " + error.ToString());
            }
        }

        void TheList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {

            //foreach (BoolStringClass item in e.NewItems) { }
                
        }

        void TheList_Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            
            BoolStringClass item = (BoolStringClass)sender;
            XElement xEle = XElement.Load(settingsFilePath);
            var fonts = from nm in xEle.Elements()
                        where (string)nm.Element("FontPath") == item.ThePath
                        select nm;
            if (item.IsSelected == false)
            {
                RemoveFont(item.ThePath);
                fonts.First().Element("IsSelected").Value = "false";
            }
            else
            {
                AddFont(item.ThePath);
                fonts.First().Element("IsSelected").Value = "true";
            }
            xEle.Save(settingsFilePath);
            
            //SortListbox();
            //MessageBox.Show(sender.ToString());
        }

        private void RemoveFont(String path)
        {
            int result = -1;
            int error = 0;
            result = RemoveFontResource(path);
            error = Marshal.GetLastWin32Error();
            if (error != 0)
            {
                lbl_statusBar.Text = "Remove ERROR " + error.ToString();
                stb_status.Background = Brushes.LightCoral;
            }
            else
            {
                lbl_statusBar.Text = "Font removed: " + path + " = " + result;
                stb_status.Background = Brushes.LightGray;
            }
        }

        private int AddFont(String path)
        {
            int result = -1;
            int error = 0;
            result = RemoveFontResource(path);
            error = Marshal.GetLastWin32Error();
            if (error != 0)
            {
                lbl_statusBar.Text = "Remove ERROR " + error.ToString();
                stb_status.Background = Brushes.LightPink;
            }
            else
            {
                lbl_statusBar.Text = "Font removed: " + path + " = " + result;
                stb_status.Background = Brushes.LightGray;
            }
            result = AddFontResource(path);
            error = Marshal.GetLastWin32Error();
            if (error != 0)
            {
                lbl_statusBar.Text = "Add ERROR " + error.ToString();
                stb_status.Background = Brushes.LightPink;
            }
            else
            {
                lbl_statusBar.Text = "Font added: " + path + " = " + result;
                stb_status.Background = Brushes.LightGray;
            }
            return error;
        }

        protected override void OnStateChanged(EventArgs e)
        {
            if (WindowState == WindowState.Minimized)
                this.Hide();
            base.OnStateChanged(e);
        }

        private void Engage_Font_Adding(String choosenFontFile)
        {
            String fontName = "";
            try
            {
                
                String fileName = Path.GetFileName(choosenFontFile);
                String fileExtension = Path.GetExtension(choosenFontFile);
                //MessageBox.Show(fileName + "   " + fileExtension);
                if (fileExtension.ToLower() == ".ttc" || fileExtension.ToLower() == ".ttf")
                {
                    PrivateFontCollection fontCol = new PrivateFontCollection();
                    fontCol.AddFontFile(choosenFontFile);
                    fontName = fontCol.Families[0].Name;
                }
                else if (fileExtension.ToLower() == ".otf")
                    fontName = "OTF";
                else if (fileExtension.ToLower() == ".fon")
                    fontName = "FON";
                else if (fileExtension.ToLower() == ".fnt")
                    fontName = "FNT";
                else if (fileExtension.ToLower() == ".fot")
                    fontName = "FOT";
                else if (fileExtension.ToLower() == ".mmm")
                    fontName = "MMM";
                else if (fileExtension.ToLower() == ".pfb")
                    fontName = "PFB";
                else if (fileExtension.ToLower() == ".pfm")
                    fontName = "PFM";
                else throw new Exception("Not a font file");

                String newFilePath = fontsFolderPath + fileName;

                lbl_statusBar.Text = newFilePath;

                String fileDesc = fileName + " (" + fontName + ")";

                Directory.CreateDirectory(fontsFolderPath);
                if (!File.Exists(newFilePath)) 
                        File.Copy(choosenFontFile, newFilePath, true);
                else
                    try
                    {
                        File.Copy(choosenFontFile, newFilePath, true);
                    }
                    catch (Exception ex) 
                    { 
                        lbl_statusBar.Text = ex.Message; 
                    }
                                

                // Try install the font.                
                if (AddFont(newFilePath) == 0)
                {
                    if (!TheList.Any(cus => cus.ThePath == newFilePath))
                    {
                        TheList.Add(new BoolStringClass { IsSelected = true, TheText = fileDesc, ThePath = newFilePath });
                        BoolStringClass item = TheList[TheList.Count - 1];
                        item.PropertyChanged += TheList_Item_PropertyChanged;
                        
                        if (File.Exists(settingsFilePath))
                        {
                            XElement xEle = XElement.Load(settingsFilePath);
                            xEle.Add(new XElement("Font",
                                        new XElement("FontName", fileDesc),
                                        new XElement("FontPath", newFilePath),
                                        new XElement("IsSelected", "true")
                                        ));
                            xEle.Save(settingsFilePath);
                        }
                        else
                        {
                            XNamespace empNM = "urn:lst-emp:emp";
                            XDocument xDoc = new XDocument(
                                        new XDeclaration("1.0", "UTF-16", null),
                                        new XElement(empNM + "Fontovi",
                                            new XElement("Font",
                                                new XElement("FontName", fileDesc),
                                                new XElement("FontPath", newFilePath),
                                                new XElement("IsSelected", "true")
                                                )));
                            xDoc.Save(settingsFilePath);
                        }
                    }
                    else
                    {                        
                        XElement xEle = XElement.Load(settingsFilePath);
                        var fonts = from nm in xEle.Elements()
                                    where (string)nm.Element("FontPath") == newFilePath
                                    select nm;

                        fonts.First().Element("IsSelected").Value = "true";

                        for (int i = 0; i<lbx_font.Items.Count; i++)
                        {
                            BoolStringClass item = (BoolStringClass) lbx_font.Items[i];
                            if (item.ToString().Equals(fileDesc)) item.IsSelected = true;
                        }
                        xEle.Save(settingsFilePath);
                    }
                }
            }
            catch (Exception ex)
            {
                lbl_statusBar.Text = ex.Message;
                stb_status.Background = Brushes.LightCoral;
            }

            
            //}
        }

        private void btn_file_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            // Create an instance of the open file dialog box.
            OpenFileDialog selectFontDualog = new OpenFileDialog();

            // Set filter options and filter index.
            selectFontDualog.Filter = "Font Files |*.ttf;*.fon;*.otf;*.ttc|All Files (*.*)|*.*";
            selectFontDualog.FilterIndex = 1;

            selectFontDualog.Multiselect = true;

            // Call the ShowDialog method to show the dialog box.
            bool? userClickedOK = selectFontDualog.ShowDialog();

            // Process input if the user clicked OK.
            if (userClickedOK == true)
            {
                foreach (string fileName in selectFontDualog.FileNames)
                {
                    Engage_Font_Adding(fileName);
                }
                SortListbox();
            }

        }


        private void lbx_file_PreviewDragOver(object sender, DragEventArgs e)
        {
            object text = e.Data.GetData(DataFormats.FileDrop);
            string[] fileNames = (string[])text;
            ListBox lb = sender as ListBox;
            if (lb != null)
            {
                foreach (string fileName in fileNames)
                {
                    Engage_Font_Adding(fileName);
                }
                
            }
        }

        private void SortListbox()
        {
            lbx_font.Items.SortDescriptions.Add(
            new System.ComponentModel.SortDescription("TheText", System.ComponentModel.ListSortDirection.Ascending));
            //lbl_statusBar.Text = "Items sorted";
        }

        private void MenuItemDelete_Click(object sender, RoutedEventArgs e)
        {
            BoolStringClass item = (BoolStringClass)lbx_font.SelectedItem;
            if (item != null) { 
                IEnumerable<XElement> fonts = null;
                XElement xEle = null;
                try
                {
                    RemoveFont(item.ThePath);
                    xEle = XElement.Load(settingsFilePath);
                    fonts = from nm in xEle.Elements()
                                where (string)nm.Element("FontName") == item.TheText
                                select nm;
                    fonts.Remove();
                    xEle.Save(settingsFilePath);
                    TheList.Remove(item);
                }
                catch (Exception ex)
                {
                    lbl_statusBar.Text = "Font: " + item.TheText + " delete ERROR " + ex.Message;
                    stb_status.Background = Brushes.LightCoral;
                }
                try
                {
                    File.Delete(item.ThePath);
                }
                catch (Exception ex)
                {
                    if (item.IsSelected) AddFont(item.ThePath);
                    xEle.Add(fonts);
                    xEle.Save(settingsFilePath);
                    TheList.Add(item);
                    lbl_statusBar.Text = "Font: " + item.TheText + " is in use, cannot remove";
                    stb_status.Background = Brushes.LightCoral;
                }
            }
            else
            { 
                lbl_statusBar.Text = "Font not selected";
                stb_status.Background = Brushes.LightCoral; 
            }
        }

        private void btn_rem_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            IList items = lbx_font.SelectedItems;
            int icount = items.Count;
            List<BoolStringClass> selectedFonts = new List<BoolStringClass>();

            foreach(var item in items)
                selectedFonts.Add((BoolStringClass)item);

            for (int i = 0; i < icount; i++)
            {
                int rb = i + 1;
                MessageBoxResult result = MessageBox.Show("Do you want to remove font from list?\n (" + rb + "/" + icount + "): " + selectedFonts[i].ToString(), "Confirmation", MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (selectedFonts[i] != null && result == MessageBoxResult.Yes)
                {
                    BoolStringClass selectedFont = selectedFonts[i];
                    IEnumerable<XElement> fonts = null;
                    XElement xEle = null;
                    try
                    {
                        RemoveFont(selectedFont.ThePath);
                        xEle = XElement.Load(settingsFilePath);
                        fonts = from nm in xEle.Elements()
                                where (string)nm.Element("FontName") == selectedFont.TheText
                                select nm;
                        fonts.Remove();
                        xEle.Save(settingsFilePath);
                        TheList.Remove(selectedFont);
                    }
                    catch (Exception ex)
                    {
                        lbl_statusBar.Text = "Font: " + selectedFont.TheText + " delete ERROR " + ex.Message;
                        stb_status.Background = Brushes.LightCoral;
                    }
                    try
                    {
                        File.Delete(selectedFont.ThePath);
                    }
                    catch (Exception ex)
                    {
                        //if (item.IsSelected) AddFont(item.ThePath);
                        xEle = XElement.Load(settingsFilePath);
                        xEle.Add(new XElement("Font",
                            new XElement("FontName", selectedFont.TheText),
                            new XElement("FontPath", selectedFont.ThePath),
                            new XElement("IsSelected", selectedFont.IsSelected)
                            ));
                        xEle.Save(settingsFilePath);
                        TheList.Add(new BoolStringClass { IsSelected = selectedFont.IsSelected, TheText = selectedFont.TheText, ThePath = selectedFont.ThePath });
                        lbl_statusBar.Text = "Font: " + selectedFont.TheText + " is in use, cannot remove. " + ex.ToString();
                        stb_status.Background = Brushes.LightCoral;
                    }
                }
                else
                {
                    lbl_statusBar.Text = "Not selected";
                    stb_status.Background = Brushes.LightCoral;
                }
            }
        }
        
    }

}
